package com.zmsoft.gulimall.product.web;

import com.zmsoft.gulimall.product.entity.CategoryEntity;
import com.zmsoft.gulimall.product.service.CategoryService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

/**
 * @auther zmsoft
 * @Created 2021/8/11 16:53
 */

@Controller
public class IndexController {

    @Autowired
    CategoryService categoryService;

    @Autowired
    RedissonClient redisson;

    /**
     * 1、每一个许缓存的数据我们都来指定要放到那个名字的缓存。【缓存的分区（按照业务类型分）】
     * 2、@Cacheable({"category"})
     *      代表当前方法的结果需要缓存，如果缓存中有。方法不用调用。
     *      如果缓存中没有，会调用方法。最后将方法的结果放入缓存
     * 3、默认行为
     *      1）、如果缓存中有，方法不用调用。
     *      2）、key默认自动生成，缓存的名字：SimpleKey []{自主生成的key值}
     *      3）、缓存的value的值。默认使用jdk序列化机制。将序列化后的数据存到redis
     *      4）、默认ttl时间 -1
     *
     *      自定义：
     *          1）、指定生成的缓存使用的key   key属性指定，接受一个SpEl
     *              SpEL的详请：https://docs.spring.io/spring/docs/5.1.12.RELEAESE/spring-framework-reference/
     *          2）、指定缓存的数据的存活时间   配置文件中修改ttl
     *          3）、将数据保存为json格式
     *                  自定义RedisCacheConfiguration即可
     * 4、Spring-Cache的不足：
     *      1）、读模式
     *          缓存穿透：查询一个null数据，解决：缓存空数据；cache-null-values=true
     *          缓存击穿：大量并发进来同时查询一个正好过期的数据。解决：加锁
     *          缓存雪崩： 大量的key同时过期。解决：加随机时间。加上过期时间。spring.cache.redis.time-to-live=3600000
     *      2）、写模式：（缓存与数据库一致）
     *          1）、读写加锁。
     *          2）、引入Canal，感知到MySQL的更新去更新数据库
     *          3）、读多写多，直接去数据库查询就行
 *          总结：
     *          常规数据（读多写少，即时性，一致性要求不高的数据）；完全可以使用Spring-Cache；写模式（只要缓存的数据有过期时间就可以）
     *          特殊数据：特殊设计
     *
     *   原理：
     *      CacheManager(RedisCacheManager)->Cache(RedisCache)->Cache负责缓存的读写
     *
     *
     *
     * @param model
     * @return
     */
    @Cacheable(value = {"category"},key = "'#root.method.name'")   //代表当前方法的结果需要缓存，如果缓存中有，方法不用调用。如果缓存中没有，会调用方法，最后将方法的结果放入缓存
    @GetMapping({"/","/index.html"})
    public String indexPage(Model model) {

        //TODO 1、查出所有的1级分类
        List<CategoryEntity> categoryEntities = categoryService.getLevel1Categorys();

        model.addAttribute("categorys", categoryEntities);
        return "index";
    }


    @ResponseBody
    @GetMapping("/hello")
    public String hello() {
        //1、获取一把锁，只要锁的名字一样，就是同一把锁
        RLock lock = redisson.getLock("my-lock");
        //2、加锁
        lock.lock();  //阻塞式等待
        //1）、锁的自动续期，如果业务超长，运行期间自动给锁续上新的30s，不用担心业务时间长，锁自动过期被删掉
        //2）、加锁的业务只要运行完成，就不会给当前锁续期，即使不手动解锁，锁默认在30s后自动删除。
        try {
            System.out.println("加锁成功，执行业务...");
            Thread.sleep(30000);
        } catch (Exception e) {

        } finally {
            //3、解锁
            lock.unlock();
        }



        return "hello";

    }

}
